// -*- c-basic-offset: 2 default-tab-width: 2 indent-tabs-mode: t -*-
// vim: autoindent tabstop=2 noexpandtab shiftwidth=2 softtabstop=2
//===------- RJITCompiler.cpp - The Reactor just in time compiler -----------===//
//
//                            The VMKit project
//
// This file is distributed under the Purdue University Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/Constants.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Function.h"
#include "llvm/Instructions.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/Analysis/DebugInfo.h"
#include "llvm/CodeGen/GCStrategy.h"
#include "llvm/CodeGen/JITCodeEmitter.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetData.h"
#include <llvm/LinkAllPasses.h>

#include "VmkitGC.h"
#include "vmkit/VirtualMachine.h"
#include "vmkit/JIT.h"
#include "Reactor.h"
#include "Evaluator.h"


#include "RJITCompiler.h"


using namespace R;
using namespace llvm;

void RJITListener::NotifyFunctionEmitted(const Function &F, void *Code, size_t Size,
		const EmittedFunctionDetails &Details) {
	Assert(F.hasGC());
	if (TheCompiler->GCInfo == NULL) {
		TheCompiler->GCInfo = Details.MF->getGMI();
	}
	Assert(TheCompiler->GCInfo == Details.MF->getGMI());
}

RJITCompiler::RJITCompiler() : RCompiler(), listener(this) {
	std::string err;

	EmitFunctionName = false;
	GCInfo = NULL;

	llvm::EngineBuilder engine (selfModule);
	TargetOptions options;
	options.NoFramePointerElim = true;
	engine.setTargetOptions(options);
	engine.setEngineKind(llvm::EngineKind::JIT);
	engine.setErrorStr(&err);
	ee = engine.create();

	rerror_on(!ee, "unable to construct llvm jit: %s", err.c_str());

	ee->RegisterJITEventListener(&listener);
	ee->DisableLazyCompilation(0);
	ee->addModule(selfModule);

	selfModule->setDataLayout(ee->getTargetData()->getStringRepresentation());
	selfModule->setTargetTriple(vmkit::VmkitModule::getHostTriple());
	selfModule->setMaterializer(this);

	for(llvm::Module::iterator cur=selfModule->begin(), end=selfModule->end(); cur!=end; cur++) {
		void* ptr = dlsym(RTLD_DEFAULT, cur->getName().data());
//					printf("%s ---> %p\n", cur->getName().data(), ptr);
		if(ptr){
			ee->updateGlobalMapping(cur, ptr);
		}
	}
	for(llvm::Module::global_iterator cur=selfModule->global_begin(), end=selfModule->global_end(); cur!=end; cur++) {
		void* ptr = dlsym(RTLD_DEFAULT, cur->getName().data());
//					printf("%s ---> %p\n", cur->getName().data(), ptr);
		if(ptr){
			ee->updateGlobalMapping(cur, ptr);
		}
	}

	initPassManager();
}

void RJITCompiler::initPassManager() {
	pm = new llvm::FunctionPassManager(selfModule);
	pm->add(new llvm::TargetData(*ee->getTargetData()));

	pm->add(llvm::createBasicAliasAnalysisPass());

	pm->add(createFunctionInlinerPass(this));
	pm->add(createStackToValuePass(this));

	pm->add(llvm::createCFGSimplificationPass());      // Clean up disgusting code
	pm->add(llvm::createPromoteMemoryToRegisterPass());// Kill useless allocas
	pm->add(llvm::createInstructionCombiningPass()); // Cleanup for scalarrepl.
	pm->add(llvm::createScalarReplAggregatesPass()); // Break up aggregate allocas
	pm->add(llvm::createInstructionCombiningPass()); // Cleanup for scalarrepl.
	pm->add(llvm::createJumpThreadingPass());        // Thread jumps.
	pm->add(llvm::createCFGSimplificationPass());    // Merge & remove BBs
	pm->add(llvm::createInstructionCombiningPass()); // Combine silly seq's
	pm->add(llvm::createCFGSimplificationPass());    // Merge & remove BBs

	pm->add(llvm::createReassociatePass());          // Reassociate expressions
	pm->add(llvm::createLoopRotatePass());           // Rotate loops.
	pm->add(llvm::createLICMPass());                 // Hoist loop invariants
	pm->add(llvm::createLoopUnswitchPass());         // Unswitch loops.
	pm->add(llvm::createInstructionCombiningPass());
	pm->add(llvm::createIndVarSimplifyPass());       // Canonicalize indvars
	pm->add(llvm::createLoopDeletionPass());         // Delete dead loops
	pm->add(llvm::createLoopUnrollPass());           // Unroll small loops*/
	pm->add(llvm::createInstructionCombiningPass()); // Clean up after the unroller
	pm->add(llvm::createGVNPass());                  // Remove redundancies
	pm->add(llvm::createMemCpyOptPass());            // Remove memcpy / form memset
	pm->add(llvm::createSCCPPass());                 // Constant prop with SCCP

	// Run instcombine after redundancy elimination to exploit opportunities
	// opened up by them.
	pm->add(llvm::createInstructionCombiningPass());
	pm->add(llvm::createJumpThreadingPass());         // Thread jumps
	pm->add(llvm::createDeadStoreEliminationPass());  // Delete dead stores
	pm->add(llvm::createAggressiveDCEPass());         // Delete dead instructions
	pm->add(llvm::createCFGSimplificationPass());     // Merge & remove BBs

	pm->doInitialization();
}

bool RJITCompiler::MaterializeModule(llvm::Module *M, std::string *ErrInfo) { 
	nyi();
	abort();
	return false;
}

bool RJITCompiler::isDematerializable(const llvm::GlobalValue *GV) const { 
	return 0; 
}

bool RJITCompiler::Materialize(llvm::GlobalValue *GV, std::string *ErrInfo) { 
	llvm::Function* F = llvm::dyn_cast<llvm::Function>(GV);

	if(!F || !F->isMaterializable())
		return false;

	generateCode(F);
	return false;
}

bool RJITCompiler::isMaterializable(const llvm::GlobalValue *GV) const { 
	const llvm::Function *F = llvm::dyn_cast<const llvm::Function>(GV);
	return F && F->isDeclaration() && !strncmp(GV->getName().data(), "r-func-", 7);
}

RJITCompiler::~RJITCompiler() {
  ee->removeModule(selfModule);
  delete ee;
}

RJITCompiler* RJITCompiler::create() {
  return new RJITCompiler();
}

void RJITCompiler::jitCompile(FunInfo* info) {
	vmkit::VmkitModule::protectIR();
	llvm::Function* func = llvmFunction(info->module, info->id);
	info->jitFun = func;

	printf("compiling %s\n", info->jitFun->getName().data());
	func->Materialize();
	void* res = ee->getPointerToFunction(func);
	jitddo(rprintf("Compiled function %s @%p or %lx\n", func->getName().data(), res, res);)
	if (!func->isDeclaration()) {
		llvm::GCFunctionInfo& GFI = GCInfo->getFunctionInfo(*func);
		vmkit::VmkitModule::addToVM(vm, &GFI, (JIT*)ee, vm->allocator, info);

		// Now that it's compiled, we don't need the IR anymore
		func->deleteBody();
  }
  vmkit::VmkitModule::unprotectIR();
	info->code = reinterpret_cast<rFunPtr>(res);
  printf("Function compiled\n");
}
